package com.ludomancy.spaceopera;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Iterator;
import java.util.Vector;
import com.ludomancy.gamelib.Accumulator;
import com.ludomancy.gamelib.ImageStrip;
import com.ludomancy.gamelib.ProbabilityDistributor;
import com.ludomancy.gamelib.Randomizer;
import com.ludomancy.gamelib.ResourceManager;
import com.ludomancy.gamelib.Utils;
import com.ludomancy.gamelib.WorldScreenProjection;
import com.ludomancy.gamelib.entity.Entity;
import com.ludomancy.gamelib.entity.EntityClassFilter;
import com.ludomancy.gamelib.entity.EntityFilter;
import com.ludomancy.gamelib.entity.World;
import com.ludomancy.spaceopera.entities.FireHazard;
import com.ludomancy.spaceopera.entities.Hazard;
import com.ludomancy.spaceopera.entities.Resource;
import com.ludomancy.spaceopera.entities.CollectableItem;
import com.ludomancy.spaceopera.entities.Star;
import com.ludomancy.spaceopera.entities.StormHazard;
import com.ludomancy.spaceopera.entities.WormHazard;

public class LandWorld extends World
{
	// this filter allows hazards to overlap hazards of different types
	// (but still forbids to overlap among same-type hazards)
	private class HazardFilter implements EntityFilter
	{
		private int typeToInclude;
		public HazardFilter(int typeToInclude)
		{
			this.typeToInclude = typeToInclude;	
		}
		
		public boolean accepts(Entity e)
		{
			if(e instanceof Hazard)
			{
				Hazard h = (Hazard) e;
				if(h.getType() != typeToInclude)
				{
					return true;
				}
			}
			return false;
		}
	}
	// resource richness
	public static ProbabilityDistributor RESOURCES_NONE;
	public static ProbabilityDistributor RESOURCES_POOR;
	public static ProbabilityDistributor RESOURCES_AVERAGE;
	public static ProbabilityDistributor RESOURCES_RICH;
	public static ProbabilityDistributor RESOURCES_LUSCIOUS;
	private ProbabilityDistributor resources;
	
	// hazards
	public static final int HAZARD_VOLCANO = 0;
	public static final int HAZARD_STORM = 1;
	public static final int HAZARD_WORM = 2;
	public static final int HAZARD_SINGULARITY = 3;
	public static final int HAZARD_CLASSES = 4;
	
	private int[] hazardDensity;
	private int[] hazardCounts = new int[HAZARD_CLASSES];
	public int[] hazards;
	private Vector timeToAdd = new Vector();
	private Vector hazardToAdd = new Vector();
	
	// lifeforms
	public static final int LIFE_NONE = 0;
	public static final int LIFE_HOSTILE = 1;
	public static final int LIFE_FRIENDLY = 2;
	public int life;
	
	private ImageStrip surfaces;
	private int surfaceTile;
	private static final int SURFACE_TYPE_WATER = 0;
	private static final int SURFACE_TYPE_BARREN = 1;
	private static final int SURFACE_TYPE_LAVA = 2;
	private static final int SURFACE_TYPE_MARSLIKE = 3;
	private static final int SURFACE_TYPE_GREEN = 4;
	private static final int SURFACE_TYPE_TECH = 5;
	private static final int SURFACE_TYPE_ORGANIC = 6;
	private static final int SURFACE_TYPE_ROCKY = 7;
	private static final int SURFACE_TYPE_ICE = 8;
	
	// name
	private String name;

	public LandWorld(Star star, int worldWidth, int worldHeight)
	{
		super(worldWidth, worldHeight);
		surfaces = new ImageStrip(ResourceManager.getImage("landTiles.png"), 24,28);
		if(RESOURCES_POOR == null)
		{
			// init planet characteristics
			RESOURCES_NONE = new ProbabilityDistributor(new int[]{0,0,0});
			RESOURCES_POOR = new ProbabilityDistributor(new int[]{70,20,10});
			RESOURCES_AVERAGE = new ProbabilityDistributor(new int[]{50,30,20});
			RESOURCES_RICH = new ProbabilityDistributor(new int[]{20,40,30});
			RESOURCES_LUSCIOUS = new ProbabilityDistributor(new int[]{0,30,60});
		}
		
		// get the characteristics of this planet
		switch(star.planetClass)
		{
			case Star.PLANET_CLASS_AIUR:
				surfaceTile = SURFACE_TYPE_TECH;
				hazards = new int[]{HAZARD_STORM};
				hazardDensity = new int[]{10};
				resources = RESOURCES_NONE;
				name = "Aiur";
				//addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;

			case Star.PLANET_CLASS_DOSADI:
				surfaceTile = SURFACE_TYPE_LAVA;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Dosadi";
				//addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;

			case Star.PLANET_CLASS_ETERNIA:
				surfaceTile = SURFACE_TYPE_ROCKY;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Eternia";
				//addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;

			case Star.PLANET_CLASS_HARMONY:
				surfaceTile = SURFACE_TYPE_BARREN;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Harmony";
				//addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;
			
			case Star.PLANET_CLASS_KHARAK:
				surfaceTile = SURFACE_TYPE_BARREN;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Kharak";
				//addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;

			case Star.PLANET_CLASS_MAGRATHEA:
				surfaceTile = SURFACE_TYPE_MARSLIKE;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Magrathea";
				break;

			case Star.PLANET_CLASS_PANDORA:
				surfaceTile = SURFACE_TYPE_WATER;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Pandora";
				break;

			case Star.PLANET_CLASS_RIVERWORLD:
				surfaceTile = SURFACE_TYPE_WATER;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Riverworld";
				break;

			case Star.PLANET_CLASS_SOLARIS:
				surfaceTile = SURFACE_TYPE_ORGANIC;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Solaris";
				break;

			case Star.PLANET_CLASS_TERMINUS:
				surfaceTile = SURFACE_TYPE_GREEN;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Terminus";
				break;
				
			case Star.PLANET_CLASS_TSACHI:
				surfaceTile = SURFACE_TYPE_BARREN;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Tschai";
				break;

			case Star.PLANET_CLASS_WEMADEIT:
				surfaceTile = SURFACE_TYPE_TECH;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Wemadeit";
				break;
				
			case Star.PLANET_CLASS_WINTER:
				surfaceTile = SURFACE_TYPE_ICE;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Winter";
				break;
				
			case Star.PLANET_CLASS_WUNDERLAND:
				surfaceTile = SURFACE_TYPE_TECH;
				hazards = new int[]{HAZARD_STORM,HAZARD_VOLCANO};
				hazardDensity = new int[]{5,10};
				resources = RESOURCES_AVERAGE;
				name = "Wunderland";
				break;
				
			case Star.PLANET_CLASS_ARRAKIS:
				surfaceTile = SURFACE_TYPE_BARREN;
				hazards = new int[]{HAZARD_STORM, HAZARD_WORM};
				hazardDensity = new int[]{5, 16};
				life = LIFE_NONE;
				resources = RESOURCES_RICH;
				name = "Dune";
				addSpecialItem(new CollectableItem(InventoryItem.TYPE_KWIZATZ));
				break;
				
			case Star.PLANET_CLASS_ANARRES:
				surfaceTile = SURFACE_TYPE_MARSLIKE;
				hazards = new int[]{HAZARD_VOLCANO, HAZARD_STORM};
				hazardDensity = new int[]{6,2};
				life = LIFE_FRIENDLY;
				resources = RESOURCES_POOR;
				name = "Anarres";
				addSpecialItem(new CollectableItem(InventoryItem.TYPE_TRANSILIENT));
				break;
				
			case Star.PLANET_CLASS_GENERIC:
			{
				switch(star.surface)
				{
					case Star.CLIMATE_FROZEN:
						hazards = new int[]{HAZARD_VOLCANO,HAZARD_STORM};
						hazardDensity = new int[]{3,4};
						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_FRIENDLY;
							resources = RESOURCES_POOR;
						}
						else
						{
							life = LIFE_FRIENDLY;
							resources = RESOURCES_AVERAGE;
						}
						break;
					case Star.CLIMATE_DRY:
						hazards = new int[]{HAZARD_VOLCANO,HAZARD_STORM};
						hazardDensity = new int[]{15,4};

						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_NONE;
							resources = RESOURCES_AVERAGE;
						}
						else
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_AVERAGE;
						}
						break;
					case Star.CLIMATE_HOT:
						hazards = new int[]{HAZARD_VOLCANO};
						hazardDensity = new int[]{15};

						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_NONE;
							resources = RESOURCES_AVERAGE;
						}
						else
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_AVERAGE;
						}
						break;
					case Star.CLIMATE_TEMPERATE:
						hazards = new int[]{HAZARD_STORM};
						hazardDensity = new int[]{5};

						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_AVERAGE;
						}
						else
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_POOR;
						}
						break;

					case Star.CLIMATE_DESERTIC:
						hazards = new int[]{HAZARD_STORM};
						hazardDensity = new int[]{5};

						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_AVERAGE;
						}
						else
						{
							life = LIFE_HOSTILE;
							resources = RESOURCES_POOR;
						}
						break;
					default:
					case Star.CLIMATE_RARE:
						hazards = new int[]{HAZARD_VOLCANO, HAZARD_STORM};
						hazardDensity = new int[]{15,8};
						if(star.size == Star.SIZE_LARGE)
						{
							life = LIFE_NONE;
							resources = RESOURCES_NONE;
						}
						else
						{
							life = LIFE_NONE;
							resources = RESOURCES_POOR;
						}
						break;
				}
			}
		}
				
		// add resources
		int resCount = (star.size+1)*(Randomizer.getRandom(1, 8));
		for (int i = 0; i < resCount; i++)
		{
			int resType = resources.getRandomEvent();
			BasicEntity e = new Resource(new Point(), resType);
			moveToUnoccupiedRandomPlace(null, e);
			push(e);
		}
	}
	
	private void addSpecialItem(CollectableItem item)
	{
		moveToUnoccupiedRandomPlace(null, item);
		add(item);
	}

	public void paint(Graphics g, WorldScreenProjection projector)
	{
		Utils.fillArea(g, (int)projector.screenWidth, (int)projector.screenHeight, surfaces, surfaceTile);
		super.paint(g, projector);
		if(name != null)
		{
			g.setColor(Color.WHITE);
			g.setFont(GameTheme.theme.title);
			Utils.drawString(g, name, 5, 2);
		}
	}
	
	public void update()
	{
		super.update();
		
		// add scheduled hazards
		for (int i = 0; i < timeToAdd.size();)
		{
			Accumulator a = (Accumulator) timeToAdd.elementAt(i);
			a.update();
			if(a.isDone())
			{
				Hazard haz = (Hazard) hazardToAdd.elementAt(i);
				moveToUnoccupiedRandomPlace(null, haz, new HazardFilter(haz.getType()));
				push(haz);
				timeToAdd.remove(i);
				hazardToAdd.remove(i);
			}
			else
			{
				i++;
			}
		}
		
		Vector myHazards = getEntities(new EntityClassFilter(Hazard.class));
		// remove dead hazards
		for (Iterator iterator = myHazards.iterator(); iterator.hasNext();)
		{	
			Hazard h = (Hazard) iterator.next();
			if(h.isConsumed())
			{
				iterator.remove();
				remove(h);
				hazardCounts[h.getType()]--;
			}
		}
		for (int i = 0; i < hazards.length; i++)
		{
			int hazardType = hazards[i];
			int diff = hazardDensity[i] - hazardCounts[hazardType];
			while(diff > 0)
			{
				Hazard haz = null;
				Accumulator timer = new Accumulator(Randomizer.getRandom(Game.forTime(100), Game.forTime(3000))); 
				switch(hazardType)
				{
					case HAZARD_WORM:
						haz = new WormHazard(new Point());
						break;
					case HAZARD_VOLCANO:
						haz = new FireHazard(new Point());
						break;
					case HAZARD_STORM:
						timer = new Accumulator(0);
						haz = new StormHazard(new Point());
						break;
					default:
				}
				if(haz != null)
				{
					hazardCounts[haz.getType()]++;
					timeToAdd.add(timer);
					hazardToAdd.add(haz);
				}
				diff--;
			}
		}
	}

}
